package com.bckj.fastboot.core.sql;

import com.bckj.fastboot.core.lang.enums.OperatorEnum;
import com.bckj.fastboot.core.util.BeanAccessor;
import com.bckj.fastboot.core.util.BeanAccessorProvider;
import com.bckj.fastboot.core.util.ConvertUtils;
import org.apache.ibatis.jdbc.SQL;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.stream.Collectors;

public class QueryConditionBuilder {

    public static <T> void buildQueryCondition(SQL sql, T bean) {
        buildQueryCondition(sql, bean, null);
    }

    @SuppressWarnings("unchecked")
    public static <T> void buildQueryCondition(SQL sql, T bean, String fieldPrefix) {
        Field[] fields = bean.getClass().getDeclaredFields();
        BeanAccessor<T> beanAccessor = (BeanAccessor<T>) BeanAccessorProvider.get(bean.getClass());
        for (Field field : fields) {
            String column;
            OperatorEnum operator = OperatorEnum.EQ;
            QueryMapping anno = field.getAnnotation(QueryMapping.class);
            if (anno != null) {
                if (!anno.exist()) {
                    continue;
                }
                column = anno.value();
                operator = anno.operator();
            } else {
                column = ConvertUtils.camelCaseToUnderline(field.getName());
            }
            Object value = beanAccessor.getValue(bean, field.getName()).orElse(null);
            if (value != null) {
                String fieldName;
                if (fieldPrefix != null) {
                    fieldName = fieldPrefix + "." + field.getName();
                } else {
                    fieldName = field.getName();
                }
                sql.WHERE(buildWhere(column, fieldName, operator, value));
            }
        }
    }

    private static String buildWhere(String column, String fieldName, OperatorEnum operator, Object value) {
        switch (operator) {
            case EQ -> {
                return column + " = #{" + fieldName + "}";
            }
            case NE -> {
                return column + " != #{" + fieldName + "}";
            }
            case GE -> {
                return column + " >= #{" + fieldName + "}";
            }
            case GT -> {
                return column + " > #{" + fieldName + "}";
            }
            case LE -> {
                return column + " <= #{" + fieldName + "}";
            }
            case LT -> {
                return column + " < #{" + fieldName + "}";
            }
            case LIKE -> {
                return column + " LIKE CONCAT('%',#{" + fieldName + "},'%')";
            }
            case LIKE_LEFT -> {
                return column + " LIKE CONCAT('%',#{" + fieldName + "})";
            }
            case LIKE_RIGHT -> {
                return column + " LIKE CONCAT(#{" + fieldName + "},'%')";
            }
            case IN -> {
                if (value instanceof Collection<?> coll) {
                    return column + " IN (" + coll.stream().map(p -> "'" + p + "'").collect(Collectors.joining(",")) + ")";
                }
                throw new RuntimeException("in操作必须传入Collection<?>");
            }
            case NOT_IN -> {
                if (value instanceof Collection<?> coll) {
                    return column + " NOT IN (" + coll.stream().map(p -> "'" + p + "'").collect(Collectors.joining(",")) + ")";
                }
                throw new RuntimeException("not_in操作必须传入Collection<?>");
            }
            case BETWEEN -> {
                Object[] array;
                if (value instanceof Collection<?> coll) {
                    if (coll.size() != 2) {
                        throw new RuntimeException("between操作必须传入两个参数");
                    }
                    array = coll.toArray();
                } else if (value instanceof Object[] arr) {
                    if (arr.length != 2) {
                        throw new RuntimeException("between操作必须传入两个参数");
                    }
                    array = arr;
                } else {
                    throw new RuntimeException("between操作必须传入Collection<?> OR Object[]");
                }
                return column + " BETWEEN '" + array[0] + "' AND '" + array[1] + "'";
            }
        }
        throw new RuntimeException("不支持的操作");
    }

}
